#include <QtGui>

#include "Arrow.h"
#include "OperationItem.h"
#include "ClassItem.h"
#include "Arrow.h"
#include <math.h>
#include "ContextMenuManager.h"
#include "../GraphScene.h"
#include "StringTools.h"

using SemanticModel::Relation;

QMap<SemanticModel::RelationType, int> Arrow::_relationMetrix;
int Arrow::_textHeight;

Arrow::Arrow(GraphScene *scene, GraphNodeItem *startItem,
		GraphNodeItem *endItem, SemanticModel::RelationType relationType, 
		QGraphicsItem *parent)
	: QGraphicsItem(parent, scene)
{
	_scene = scene;
	_startItem = startItem;
	_endItem = endItem;

	QString name  = typeid(_startItem).name();
	
	_fromClass = typeid(*_startItem) == typeid(ClassItem);
	_toClass = typeid(*_endItem) == typeid(ClassItem);

	if (relationType != SemanticModel::NoneRelation)
		_relation = relationType;
	else if (_fromClass)
		_relation = _toClass 
			? SemanticModel::ClassClassRelations[0]
			: SemanticModel::ClassOperationRelations[0];
	else
		_relation = _toClass 
			? SemanticModel::OperationClassRelations[0]
			: SemanticModel::OperationOperationRelations[0];
		

	setFlag(QGraphicsItem::ItemIsSelectable, true);
	const QColor _color(Qt::black);
	_pen = QPen(_color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
	_startItem->addArrow(this);
	_endItem->addArrow(this);
		
	setZValue(-1000.0);
}

Arrow::~Arrow()
{
	_startItem->deleteArrow(this);
	_endItem->deleteArrow(this);
}

QRectF Arrow::boundingRect() const
{
	return _fullShape.boundingRect();
}

int Arrow::relationNameWidth() const
{
	QMap<SemanticModel::RelationType, int>::const_iterator i = _relationMetrix.find(relation());
	if (i == _relationMetrix.end())
	{
		QFontMetrics fm(_startItem->font());
		int width = fm.width(relationName());
		_textHeight = fm.height();
		i = _relationMetrix.insert(relation(), width);
	}
	return *i;		
}

QPainterPath Arrow::shape() const
{

	return _fullShape;
}

void Arrow::updatePosition()
{
	QLineF centerLine(
		mapFromItem(_endItem, _endItem->boundingRect().center()),
		mapFromItem(_startItem, _startItem->boundingRect().center()));

	if (_currentMetrix == centerLine)
	{
		return;
	}
	_currentMetrix = centerLine;
	
	QPolygonF endPolygon = _endItem->shape().toFillPolygon();
	QPointF p1 = endPolygon.first() + _endItem->pos();
	QPointF intersectPoint;
	for (int i = 1; i < endPolygon.count(); ++i)
	{
		QPointF p2 = endPolygon.at(i) + _endItem->pos();
		QLineF polyLine = QLineF(p1, p2);
		QLineF::IntersectType intersectType = polyLine.intersect(centerLine, &intersectPoint);
		if (intersectType == QLineF::BoundedIntersection)
			break;
		p1 = p2;
	}

	_line = QLineF(intersectPoint, centerLine.p2());

	const qreal arrowSize = 20;
	const qreal Pi = 3.14;
	double angle = ::acos(_line.dx() / _line.length());
	if (_line.dy() >= 0)
		angle = (Pi * 2) - angle;
	
	QPointF arrowP1 = _line.p1() + 
		QPointF(sin(angle + Pi / 3) * arrowSize,
		cos(angle + Pi / 3) * arrowSize);
	QPointF arrowP2 = _line.p1() + 
		QPointF(sin(angle + Pi - Pi / 3) * arrowSize,
		cos(angle + Pi - Pi / 3) * arrowSize);
		
	_lineShape = QPainterPath();
	_lineShape.addPolygon(QPolygonF() << arrowP2 << _line.p1() << arrowP1 << arrowP2);
	_lineShape.moveTo(QLineF(arrowP1, arrowP2).pointAt(0.5));
	_lineShape.lineTo(_line.p2());


	QPainterPathStroker ps;
	ps.setCapStyle(_pen.capStyle());
	ps.setWidth(5);
	ps.setDashOffset(4);
	ps.setJoinStyle(Qt::MiterJoin);
	ps.setMiterLimit(_pen.miterLimit());		
	_selectionShape = ps.createStroke(_lineShape);
	
	QRectF textRect(0, 0, relationNameWidth(), _textHeight);
	textRect.moveCenter(_selectionShape.boundingRect().center());
	_fullShape = _selectionShape;
	_fullShape.addRect(textRect);

	prepareGeometryChange();
	
	update();
}

QString Arrow::relationName() const
{
	return toQString(SemanticModel::Relation::nameOf(_relation));
}

void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *style, QWidget *widget)
{
   if (_startItem->collidesWithItem(_endItem))
		return;

	updatePosition();
			
			
	painter->setRenderHint(QPainter::Antialiasing);
	if (isSelected()) 
	{
	painter->setPen(QPen(Qt::lightGray, 1, Qt::DashLine));
		painter->drawPath(_selectionShape);
		
	}	

	
   
	painter->setPen(_pen);
	painter->setBrush(_pen.color());


	painter->setPen(Qt::gray);
	//QPolygonF poligone = arrowHead;
	//painter->drawLine(_line);
	//painter->drawPolygon(arrowHead);
	painter->drawPath(_lineShape);
	//painter->drawPolygon(arrowHead);
	
	painter->setFont(_startItem->font());
	painter->setPen(Qt::black);

	painter->drawText(boundingRect().toAlignedRect(),
		Qt::AlignHCenter | Qt::AlignVCenter, 
		relationName());

}



void Arrow::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
	if (_scene->mode() == GraphScene::ReadOnly)
		return;

	ContextMenuTarget menuType = RelationMenu;
	if (_fromClass)
		menuType = (ContextMenuTarget)(menuType | FromClassMenu);
	if (_toClass)
		menuType = (ContextMenuTarget)(menuType | ToClassMenu);
	QMenu& menu = ContextMenuManager::instance().get(menuType);

	scene()->clearSelection();
	setSelected(true);
	QAction* result = menu.exec(event->screenPos());

	if (result == ContextMenuManager::instance().deleteAction())
	{
		scene()->removeItem(this);
	}
	else if (result)
	{
		int data = result->data().toInt();
		_relation = (RelationType)data;
		prepareGeometryChange();
	}
}
